#ifndef _PARSEHELPER_
#define _PARSEHELPER_

#include <stack>
#include <set>
#include "tree/VarDeclaration.h"
#include "tree/MethodDefinition.h"
#include "tree/Statement.h"
#include "tree/FunctionDefinition.h"
#include "tree/ClassDefinition.h"

using namespace std;

// Třída určená k podobnému použití jako CalcContext v příkladu.
class ParseHelper
{
public:
    // tahle pole slouží k uchování seznamu něčeho - mělo by se používat v gramatice u rekurzivních pravidel, čili tam,
    // kde je potřeba dostat seznam něčeho (-> notes.txt)
    // v gramatice by se u akcí mělo vždy zajistit, že se pole po použití vyprázdní
    vector<FieldDeclaration*> fCurrentFieldDeclarations;
	vector<TreeNode*> fCurrentVarDeclarations;
    vector<MethodDefinition*> fCurrentMethodDefinitions;
    vector<Variable*> fCurrentParameterDeclarations;
    stack<vector<TreeNode*> > fCallParamsStack;
    stack<vector<TreeNode*> > fCompStmtListStack;
    vector<string> fCurrentParentClasses;

    // tohle je globální pole definic funkcí - tam by se měly funkce vložit při parsování
    set<FunctionDefinition*> fAllFunctionDefinitions;

    // všechny definice tříd
    set<ClassDefinition*> fAllClassDefinitions;

    ParseHelper();
    ~ParseHelper();

    // Provede zakladni semantickou analyzu.
    // Pro vice informaci vizte komentare k nasledujicim metodam, ktere tato vola:
    // - CheckFunctions
    // - CheckClasses
    // - CheckClassConstructions
    // - CheckFunctionCalls
    // - CheckVariables
    // - CheckExpressionTypes
    void DoBasicSemanticChecks();

    // Vrati definici funkce, ktera je startovacim bodem programu (funkce main).
    FunctionDefinition* GetEntryPoint() const { return fMain; }

	void NewCallContext();
    void NewCompStmtContext();

	void AddDefinitionParameter(Variable * v);
	void AddCallParam(TreeNode * e);
	void AddVar(Statement * v);
	void AddBaseClass(string s);
	void AddFunction(FunctionDefinition * f);
	void AddMethod(MethodDefinition * m);
	void AddField(string s);
	void AddClass(ClassDefinition * c);
	void AddStatement(TreeNode * s);
    void AddStringConstant(string* s) { fStringConstants.push_back(s); }
    void AddCalledFunctionName(string s) { fCalledFunctionNames.push_back(s); }

	vector<TreeNode*> GetCallParams();
	vector<TreeNode*> GetVarDeclarations();
	vector<Variable*> GetDefinitonParams();
	vector<TreeNode*> GetStatements();
	vector<FieldDeclaration*> GetFields();
	vector<MethodDefinition*> GetMethods();
	vector<string> GetBaseClasses();
    vector<string*> GetStringConstants() const { return fStringConstants; }
    vector<string> GetCalledFunctionNames() const { return fCalledFunctionNames; }
    void SanitizeString(string** oInput);

private:
    // Zkontroluje deklarovane funkce.
    // Kontroluje, zda neexistuji dve nebo vice funkci se stejnym nazvem a poctem parametru.
    // Podporujeme pretezovani funkci, tedy muze existovat vice funkci se stejnym nazvem, ale musi se lisit poctem parametru.
    void CheckFunctions();

    // Zkontroluje tridy.
    // Kontroluje, zda neexistuji dve nebo vice trid se stejnym nazvem.
    // V ramci trid kontroluje i jejich metody (stejne jako u funkci i u metod podporujeme pretezovani).
    // TODO: az budu implementovat dedicnost, tak souvisejici veci by se mely vyresit taky tady.
    void CheckClasses();

    // Zkontroluje instanciovani trid.
    // Kontroluje, zda pozadovana trida existuje a jestli ma konstruktor s pozadovanym poctem parametru.
    void CheckClassConstructions();

    // Zkontroluje volani funkci.
    // Kontroluje, jestli pozadovana funkce existuje a jestli ma odpovidajici pocet parametru.
    void CheckFunctionCalls();

    // Zkontroluje pouziti promennych.
    // Pro globalni funkce:
    // - jeste pred kontrolou tela se zkontroluji vstupni promenne, jestli maji unikatni nazvy, pokud ne, vyhodi se vyjimka
    //   (jak by se pak takove promenne asi mely pouzivat v tele?)
    // - projde definici funkce a postupne se sestavuje seznam deklarovanych promennych,ktery se ulozi
    //   do FunctionDefinition::fLocalVars - budou tam i jmena vstupnich promennych (v okamziku volani funkce se parametry
    //   vlastne stavaji lokalnimi promennymi - pokud bychom podporovali predavani hodnot referenci, tak by se to muselo asi
    //   nejak upravit)
    // - pokud se pri prochazeni zjisti, ze je nejaka promenna pouzita bez deklarace, vyhodi se vyjimka
    // - stejne tak se kontroluje vicenasobna deklarace
    // Pro metody:
    // - stejna kontrola vstupnich promennych jako u funkci
    // - stejna kontrola lokalnich promennych jako u funkci, pricemz:
    //   - pri kontrole jsou automaticky deklarovany fieldy prislusne tridy, ktere tak mohou byt v tele metody pouzity jako
    //     jakakoliv jina promenna
    //   - zaroven je automaticky deklarovana promenna this, cili pristup k fieldum je mozny i pomoci this, pristup k metodam
    //     teto tridy je mozny POUZE pomoci this
    //   - do fLocalVars jsou stejne jako u funkci vlozeny jen parametry metody a lokalni promenne
    void CheckVariables();

    // Provede kontrolu typu vyrazu.
    // Kontroluje se:
    // - pokud se volani funkce / metody pouziva jako vyraz, kontroluje se, zda funkce / metoda skutecne vraci nejakou hodnotu
    // - podminky prikazu if, cyklu while a for: podminka musi byt typu Boolean
    // - přístup k fieldům tříd: pokud lze zjistit typ proměnné (přesněji řečeno třídu), kontroluje se, zda požadovaný field
    //   ve třídě existuje
    // - volání metod: analogická kontrola jako u fieldů
    // - v předchozích dvou případech musí být objektová proměnná skutečně objektem
    //   (není možné něco jako: var x = 5; x.CallMePlease();
    // - unární mínus: výraz musí být typu LongInteger nebo Double,
    //   výsledek je stejného typu jako výraz
    // - binární operace:
    //   - operace +,-,*,/ - operandy musí být LongInteger nebo Double
    //     - pokud je jeden z operandů Double, výsledek je Double
    //   - operace +: může jít i o stringy
    //   - operace <,>,<=,>=
    //     - pouze ciselne typy, vysledek je Boolean
    //   - operace !=,=
    //     - oba dva operandy musi byt cislo, oba dva string nebo oba dva Boolean
    //     - vysledek je Boolean
    // Pokud při kontrole nelze typ nějaké části výrazu vyhodnotit, kontrola se považuje za úspěšnou.
    // Při této analýze nelze určit typy:
    // - vstupních proměnných funkcí a metod
    // - fieldů.
    void CheckExpressionTypes();

    // Vyresi dedicnosti trid.
    void ResolveInheritance();

    // Do definic funkci vlozi preddefinovane (built-in) funkce. Volano z konstruktoru.
    void FillPredefinedFunctions();

    // Odkaz na funkci main().
    FunctionDefinition* fMain;

    vector<string*> fStringConstants;
    vector<string> fCalledFunctionNames;
};

#endif
